package com.lyfei.ydc.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import com.lyfei.ydc.core.LocalUser;
import com.lyfei.ydc.domain.dto.OrderDto;
import com.lyfei.ydc.domain.dto.SkuInfoDto;
import com.lyfei.ydc.domain.entity.Order;
import com.lyfei.ydc.domain.entity.OrderSku;
import com.lyfei.ydc.domain.entity.Sku;
import com.lyfei.ydc.domain.vo.OrderAddressVo;
import com.lyfei.ydc.domain.vo.OrderSimplifyVo;
import com.lyfei.ydc.domain.vo.OrderVo;
import com.lyfei.ydc.enumeration.OrderStatus;
import com.lyfei.ydc.exception.http.NotFoundException;
import com.lyfei.ydc.exception.http.ParameterException;
import com.lyfei.ydc.logic.OrderChecker;
import com.lyfei.ydc.mapper.OrderMapper;
import com.lyfei.ydc.utils.CommonUtil;
import com.lyfei.ydc.utils.GenericAndJson;
import com.lyfei.ydc.utils.OrderUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service("orderService")
public class OrderService extends BaseService<OrderMapper, Order> {
    @Autowired
    private SkuService skuService;

    @Autowired
    private OrderSkuService orderSkuService;

    @Value("3600")
    private Integer payTimeLimit;

    @Value("100")
    private int maxSkuLimit;

    @Transactional
    public Long placeOrder(Long uid, OrderDto orderDTO, OrderChecker orderChecker) {
        String orderNo = OrderUtil.makeOrderNo();
        Calendar now = Calendar.getInstance();
        Calendar now1 = (Calendar) now.clone();
        Date expiredTime = CommonUtil.addSomeSeconds(now, this.payTimeLimit).getTime();

        Order order = new Order();
        order.setOrderNo(orderNo);
        order.setUserId(uid);
        order.setStatus(OrderStatus.UNPAID.value());
        order.setTotalPrice(orderDTO.getTotalPrice());
        order.setFinalTotalPrice(orderDTO.getFinalTotalPrice());
        order.setTotalCount(orderChecker.getTotalCount().longValue());
        order.setSnapAddress(GenericAndJson.objectToJson(orderDTO.getAddress()));
        order.setSnapImg(orderChecker.getLeaderImg());
        order.setSnapTitle(orderChecker.getLeaderTitle());
        order.setExpiredTime(expiredTime);
        order.setPlacedTime(now1.getTime());

        this.save(order);

        orderChecker.getOrderSkuList().forEach(orderSku -> {
            orderSku.setOrderId(order.getId());
            orderSkuService.save(orderSku);
            this.reduceStock(orderSku);
        });

        return order.getId();
    }

    public void updateOrderPrepayId(Long orderId, String prePayId) {
        Optional<Order> order = Optional.ofNullable(this.getById(orderId));
        order.ifPresent(o -> {
            o.setPrepayId(prePayId);
            this.updateById(o);
        });
        order.orElseThrow(() -> new ParameterException(10007));
    }

    public IPage<OrderSimplifyVo> getByStatus(Integer current, Integer size, Integer status) {
        Long uid = LocalUser.getUser().getId();
        IPage<Order> orderPage = new Page<>(current, size);
        if (status == null || status == OrderStatus.All.value()) {
            orderPage = this.page(new Page<>(current, size), new LambdaQueryWrapper<Order>()
                    .eq(Order::getUserId, uid));
        } else {
            orderPage = this.page(new Page<>(current, size), new LambdaQueryWrapper<Order>()
                    .eq(Order::getUserId, uid).eq(Order::getStatus, status));
        }

        return getOrderSimplifyVoIPage(current, size, orderPage);
    }

    public IPage<OrderSimplifyVo> getUnpaid(Integer current, Integer size) {
        IPage<Order> orderIPage = this.page(new Page<>(current, size), new LambdaQueryWrapper<Order>()
                .eq(Order::getStatus, OrderStatus.UNPAID.value())
                .gt(Order::getExpiredTime, new Date())
                .eq(Order::getUserId, LocalUser.getUser().getId()));

        return getOrderSimplifyVoIPage(current, size, orderIPage);
    }

    private IPage<OrderSimplifyVo> getOrderSimplifyVoIPage(Integer current, Integer size, IPage<Order> orderPage) {
        IPage<OrderSimplifyVo> orderSimplifyVoPage = new Page<>(current, size);
        List<OrderSimplifyVo> orderSimplifyVoList = new ArrayList<>();
        orderPage.getRecords().forEach(item -> {
            OrderSimplifyVo orderSimplifyVo = new OrderSimplifyVo();
            BeanUtils.copyProperties(item, orderSimplifyVo);
            orderSimplifyVo.setPeriod(Long.valueOf(this.payTimeLimit));
            orderSimplifyVoList.add(orderSimplifyVo);
        });
        orderSimplifyVoPage.setRecords(orderSimplifyVoList);
        orderSimplifyVoPage.setTotal(orderPage.getTotal());
        return orderSimplifyVoPage;
    }


    public OrderChecker isOk(Long uid, OrderDto orderDTO) {
        if (orderDTO.getFinalTotalPrice().compareTo(new BigDecimal("0")) <= 0) {
            throw new ParameterException(50011);
        }

        List<Long> skuIdList = orderDTO.getSkuInfoList()
                .stream()
                .map(SkuInfoDto::getId)
                .collect(Collectors.toList());

        List<Sku> skuList = skuService.getSkuListByIds(skuIdList);

        OrderChecker orderChecker = new OrderChecker(
                orderDTO, skuList, this.maxSkuLimit
        );
        orderChecker.isOK();
        return orderChecker;
    }

    public OrderVo getOrderDetail(Long orderId) {
        OrderVo orderVo = new OrderVo();

        Order order = this.getById(orderId);
        if (order == null) {
            throw new NotFoundException(50009);
        }
        BeanUtils.copyProperties(order, orderVo);

        List<OrderSku> orderSkuList = orderSkuService.list(new LambdaQueryWrapper<OrderSku>()
                .eq(OrderSku::getOrderId, orderId));
        orderVo.setOrderSkuList(orderSkuList);

        OrderAddressVo o = GenericAndJson.jsonToObject(order.getSnapAddress(),
                new TypeReference<OrderAddressVo>() {
                });

        orderVo.setOrderAddress(o);
        orderVo.setPeriod(Long.valueOf(this.payTimeLimit));

        return orderVo;
    }

    private void reduceStock(OrderSku orderSku) {
        Sku sku = skuService.getById(orderSku.getSkuId());
        if (sku.getStock() < orderSku.getCount()) {
            throw new ParameterException(50003);
        }

        sku.setStock(sku.getStock() - orderSku.getCount());
        skuService.updateById(sku);
    }
}
